﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using FlyingWormConsole3.LiteNetLibEditor;
using Gp.Scripts.Core;
using Gp.Scripts.Core.Res;
using Gp.Scripts.Utils;
using Helpers;
using QFramework;
using UnityEngine;
using Object = UnityEngine.Object;

public static class CommonUtils
{
    public static List<Type> GetTargetType(this Assembly assembly,              Type attribute = null,
        bool                                             filterAbstract = true, Type baseClass = null)
    {
        var res = new List<Type>(assembly.GetTypes().Where(x =>
        {
            bool flag                   = true;
            if (attribute != null) flag &= x.GetCustomAttribute(attribute) != null;
            if (filterAbstract) flag    &= !x.IsAbstract;
            if (baseClass != null) flag &= baseClass.IsAssignableFrom(x) || x.HasImplementedRawGeneric(baseClass);
            return flag;
        }));
        return res;
    }

    public static IEnumerable<T> LoadAssetBundle<T>(Type t) where T : Object
    {
        var          resLoader  = ResLoader.Allocate();
        const string bundleName = "BundleName";


        FieldInfo[] fieldInfo = t.GetFields();

        string bundleNameString = "";

        foreach (var f in fieldInfo)
        {
            if (f.Name == bundleName)
            {
                bundleNameString = f.GetRawConstantValue().ToString();
                continue;
            }

            string value = f.GetRawConstantValue().ToString();

            if (!string.IsNullOrEmpty(bundleNameString))
                yield return resLoader.LoadSync<T>(bundleNameString, value);
            else
                yield return resLoader.LoadSync<T>(value);
        }
    }


    public static IEnumerator YieldPlay(this Animator animator, string animationName)
    {
        animator.Play(animationName);
        yield return new WaitForSeconds(animator.ReturnAnimatorTimer(animationName));
    }


    public static IEnumerator YieldPlayClip(this Animator animator, AnimationClip clip)
    {
        if (animator == null || clip == null) yield break;
        animator.PlayClip(clip);
        yield return new WaitForSeconds(clip.length);
    }

    public static void PlayClip(this Animator animator, AnimationClip clip)
    {
        var overrideController = new AnimatorOverrideController(animator.runtimeAnimatorController);
        overrideController["Roll"]         = clip;
        animator.runtimeAnimatorController = overrideController;
        animator.Play("Roll");
    }


    public static float ReturnAnimatorTimer(this Animator animator, string animatorName)
    {
        AnimationClip[] clips = animator.runtimeAnimatorController.animationClips;
        return (from clip in clips where clip.name.Equals(animatorName) select clip.length).FirstOrDefault();
    }


    public static void SafeCall(Action func, string label, string msg)
    {
        try
        {
            func?.Invoke();
        }
        catch (Exception e)
        {
            LogHelper.LogError(label, $"{msg}\n {e}");
        }
    }

    public static T GetOrCreate<T>(this Component self, string targetName) where T : Object
    {
        return GetOrCreate<T>(self.transform, targetName);
    }

    public static T GetOrCreate<T>(this GameObject self, string targetName) where T : Object
    {
        return GetOrCreate<T>(self.transform, targetName);
    }
    
    public static T GetOrCreate<T>(this Transform self, string targetName) where T : Object
    {
        targetName = string.IsNullOrWhiteSpace(targetName) ? "AutoCreate" : targetName;
        var target = self.transform.Find(targetName);
        if (!target)
        {
            Transform parent = self.transform;
            foreach (var name in targetName.Split("/"))
            {
                var node = new GameObject(name).transform;
                node.SetParent(parent, false);
                parent = node;
            }
        }
        target = self.transform.Find(targetName);
        if (typeof(T) == typeof(GameObject))
        {
            return target.gameObject as T;
        }
        else
        {
            return target.gameObject.GetOrAddComponent(typeof(T)) as T;
        }
    }
}